﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GISCore.Geometry.Partition;

namespace GISCore.Geometry.Merge
{
    public class LinkedMerge : IMergable
    {
        private List<PostPolygon> _globalBuffer = new List<PostPolygon>();
        public Layer merge(Dictionary<int,Cell> cells)
        {

            int i, j, k;

            /* add all of the postPosygons in each cell in  a single buffer.*/
            foreach (var cell in cells.Values)
            {
                foreach (var p in cell.postPolys)
                {
                    _globalBuffer.Add(p);
                }
            }
            /*merge neighbor cells in a left-to-right, top-to-bottom fashion*/
            foreach (var cell in cells.Values)
            {
                if (cell.getLeftCell() != 0)
                {
                    _merge(cell, cells[cell.getLeftCell()], 1);
                }
                if (cell.getRightCell() != 0)
                {
                    _merge(cell, cells[cell.getRightCell()], 2);
                }
                if (cell.getTopCell() != 0)
                {
                    _merge(cell, cells[cell.getTopCell()], 3);
                }
                if (cell.getBottomCell() != 0)
                {
                    _merge(cell, cells[cell.getBottomCell()], 4);
                }
            }
            Console.WriteLine("output.......");
            foreach(var p in _globalBuffer){
                Console.WriteLine(p);
            }
            return _constructLayer(_globalBuffer);

        }
        
        /// <summary>
        /// merge postPolygons in neighbour cells
        /// </summary>
        /// <param name="baseCell"></param>
        /// <param name="targetCell"></param>
        /// <param name="direction"></param> 1: left to right; 2: right to left; 3:bottom to top; 4: top to bottom;
        private void _merge(ICellable baseCell,ICellable targetCell,int direction){
            List<LinkedPoint> baseGridIntersectPoints=null;
            List<LinkedPoint> targetGridIntersectPoints=null;
            int i, j;
            switch(direction){
                case 1: 
                    baseGridIntersectPoints = (List<LinkedPoint>)baseCell.getLeftLinkedGridIntersectPoint();
                    targetGridIntersectPoints = (List<LinkedPoint>)targetCell.getRightLinkedGridIntersectPoint();
                    break;
                case 2:
                    baseGridIntersectPoints = (List<LinkedPoint>)baseCell.getRightLinkedGridIntersectPoint();
                    targetGridIntersectPoints = (List<LinkedPoint>)targetCell.getLeftLinkedGridIntersectPoint();
                    break;
                case 3:
                    baseGridIntersectPoints = (List<LinkedPoint>)baseCell.getTopLinkedGridIntersectPoint();
                    targetGridIntersectPoints = (List<LinkedPoint>)targetCell.getBottomLinkedGridIntersectPoint();
                    break;
                case 4:
                    baseGridIntersectPoints = (List<LinkedPoint>)baseCell.getBottomLinkedGridIntersectPoint();
                    targetGridIntersectPoints = (List<LinkedPoint>)targetCell.getTopLinkedGridIntersectPoint();
                    break;
                default:
                    return;
            }

            if (baseGridIntersectPoints.Count != 0)
            {
                for (i = 0; i < baseGridIntersectPoints.Count; i++)
                {
                    for (j = 0; j < targetGridIntersectPoints.Count; j++)
                    {
                        LinkedPoint p = baseGridIntersectPoints[i];
                        LinkedPoint p1 = targetGridIntersectPoints[j];
                        LinkedPoint p2 = null;
                        LinkedPoint p3 = null;
                        if (p.getPoint().Equals(p1.getPoint()))
                        {
                            baseGridIntersectPoints.Remove(p);
                            targetGridIntersectPoints.Remove(p1);

                            if (p.getPostPolygon().getHead() == p1.getPostPolygon().getHead())
                            {
                                if (p.getNext() == p1) 
                                {
                                    p2 = p.getPre();
                                    p3 = p1.getNext();
                                    p2.getPre().setNext(p3.getNext());
                                    p3.getNext().setPre(p2.getPre());
                                    Console.WriteLine("original polygon");
                                    Console.WriteLine(p.getPostPolygon());
                                    baseGridIntersectPoints.Remove(p2);
                                    targetGridIntersectPoints.Remove(p3);
                                }
                                else if (p1.getNext() == p)
                                {
                                    p2 = p.getNext();
                                    p3 = p1.getPre();
                                    p3.getPre().setNext(p2.getNext());
                                    p2.getNext().setPre(p3.getPre());
                                    Console.WriteLine("original polygon");
                                    Console.WriteLine(p.getPostPolygon());
                                    baseGridIntersectPoints.Remove(p2);
                                    targetGridIntersectPoints.Remove(p3);
                                }
                                continue;
                            }

                            if( (p.getNext().getPoint() is VirtualIntersectPoint) ||(p.getNext().getPoint() is GridIntersectPoint))
                            {
                                p2 = p.getNext();
                                p3 = p1.getPre();
                                p.getPre().setNext(p1.getNext());
                                p1.getNext().setPre(p.getPre());
                                p2.getNext().setPre(p3.getPre());
                                p3.getPre().setNext(p2.getNext());
                                Console.WriteLine("original polygon");
                                Console.WriteLine(p.getPostPolygon());
                                p1.getPostPolygon().setHead(p.getPostPolygon().getHead());
                                Console.WriteLine("modified polygon");
                                Console.WriteLine(p1.getPostPolygon());
                                baseGridIntersectPoints.Remove(p2);
                                targetGridIntersectPoints.Remove(p3);

                            }
                            else
                            {
                                p2 = p.getPre();
                                p3 = p1.getNext();
                                p.getNext().setPre(p1.getPre());
                                p1.getPre().setNext(p.getNext());
                                p2.getPre().setNext(p3.getNext());
                                p3.getNext().setPre(p2.getPre());
                                p1.getPostPolygon().setHead(p.getPostPolygon().getHead());
                                baseGridIntersectPoints.Remove(p2);
                                targetGridIntersectPoints.Remove(p3);
                            }
                            _globalBuffer.Remove(p1.getPostPolygon());
                        }
                    }
                }
            }
            
        }

        public Layer _constructLayer(IEnumerable<PostPolygon> postPolygons){
            List <Polygon> polys=new List<Polygon>();
            foreach(var postPolys in postPolygons){
                polys.Add(postPolys.Convert(Constants.overLayerId));
            }
            Layer overlayer=new Layer(Constants.overLayerId,polys);
            overlayer.SetBoundingBox(new Point(1, 1), new Point(1, 2));

            return overlayer;
        }
    }
}
